package com.log.demo.common.log;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.util.ReadOnlyStringMap;

import java.nio.charset.Charset;
import java.time.Instant;
import java.util.*;

/**
 * @author weijinhao
 * @date 2023/11/18 22:01
 */
@Plugin(name = "Log4jEncodeLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
public class LogPlugin extends AbstractStringLayout {

    private String formt = "[level][data][method] [dltag]";
    private PatternLayout patternLayout;

    private List<String> logKey;


    protected LogPlugin(Charset charset,String pattern) {
        super(charset);
        patternLayout = PatternLayout.newBuilder().withPattern(pattern).build();
        logKey = new ArrayList<>();
        logKey.add(LogTag.traceIdName);
        logKey.add(LogTag.spanIdName);
        logKey.add(LogTag.caller_func);
        logKey.add(LogTag.callee_func);
        logKey.add(LogTag.errno);
        logKey.add(LogTag.errmsg);
        logKey.add(LogTag.proc_time);
    }

    protected LogPlugin(Charset aCharset, byte[] header, byte[] footer) {
        super(aCharset, header, footer);

    }

    protected LogPlugin(Configuration config, Charset aCharset, Serializer headerSerializer, Serializer footerSerializer) {
        super(config, aCharset, headerSerializer, footerSerializer);
    }

    @Override
    public String toSerializable(LogEvent event) {
        String dltag = LogTag.dltag;
        StringBuffer log = new StringBuffer();
        //[%level][%data][%method] %DLTAG
        final StackTraceElement element = event.getSource();
        String logString = new String(formt);
        logString = logString.replace("level",event.getLevel().name());
        Instant instant = Instant.ofEpochMilli(event.getInstant().getEpochMillisecond());
        logString = logString.replace("data", instant.toString());
        logString = logString.replace("method",element.toString());

        ReadOnlyStringMap contextData = event.getContextData();
        Map<String, String> contextMap = contextData.toMap();
        String dltagContent = contextMap.get(dltag);
        if(dltagContent == null) {
            dltagContent = "_undef";
        }
        logString = logString.replace(dltag,dltagContent);
        log.append(logString);
        HashMap<String, String> sortMap = new HashMap<>();
        sortMap.putAll(contextMap);

        for (String keyName : logKey) {
            String value = sortMap.get(keyName);
            String tag = "||" + keyName + "=" + (value == null ? "" : value);
            log.append(tag);
            sortMap.remove(keyName);
        }

        for (Map.Entry<String, String> entry : sortMap.entrySet()) {
            if(!StringUtils.equals(dltag,entry.getKey())) {
                String key = entry.getKey();
                String value = entry.getValue();
                String tag = "||" + key + "=" + (value == null ? "" : value);
                log.append(tag);
            }
        }

        String message = patternLayout.toSerializable(event);
        log.append("||" + "_msg=" + message );
        return log.toString();
    }

    @PluginFactory
    public static Layout createLayout(
            @PluginAttribute(value = "pattern") final String pattern,
            @PluginAttribute(value = "charset") final Charset charset) {
        return new LogPlugin(charset,pattern);
    }
}
